#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

#ifdef _WIN32
#include <direct.h>
#define mkdir(path) _mkdir(path)
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif

#define MAX_FILENAME_LENGTH 256
#define RLE_MAGIC_NUMBER 0x1A5A
// 存储压缩文件中每个文件或目录的相关信息
typedef struct {
    char filename[MAX_FILENAME_LENGTH];
    long offset;     //偏移量
    long compressed_size;
    long original_size;
    int is_directory;    //一个标志位，用于判断该条目是文件还是目录 真 目录 假 文件
} FileHeader;

typedef struct {
    int magic_number;  //魔数 若魔数不匹配会终止解压缩程序
    int file_count;
    time_t timestamp;    //时间戳
} ArchiveHeader;

// RLE 解压缩实现
long rle_decompress(FILE *in, FILE *out, long compressed_size) {
    int current_char;
    long bytes_read = 0;
    long original_size = 0;

    while (bytes_read < compressed_size) {
        current_char = fgetc(in);
        bytes_read++;

        if (current_char == 0xFF && bytes_read + 2 <= compressed_size) {
            int count = fgetc(in);
            bytes_read++;
            int byte = fgetc(in);
            bytes_read++;
            for (int i = 0; i < count; i++) {
                fputc(byte, out);
                original_size++;
            }
        } else {
            fputc(current_char, out);
            original_size++;
        }
    }
    return original_size;
}

// 创建指定路径的目录，如果路径中的父目录不存在，也会递归创建。
void create_directory(const char *path) {
    if (path == NULL || *path == '\0') return;

    char temp[MAX_FILENAME_LENGTH];
    strncpy(temp, path, MAX_FILENAME_LENGTH - 1);

    char *p = temp;
    while (*p) {
        if (*p == '/') {
            *p = '\0';
            mkdir(temp);
            *p = '/';
        }
        p++;
    }
    mkdir(temp);
}

//读取压缩文件 解析文件头信息 解压缩文件并保存到指定路径
int main(int argc, char *argv[]) {
    clock_t start_time = clock();  // 记录开始时间

    if (argc != 2) {
        printf("用法: %s <压缩文件.rzp>\n", argv[0]);
        return 1;
    }

    FILE *archive = fopen(argv[1], "rb");
    if (!archive) return 1;

    ArchiveHeader header;
    if (fread(&header, sizeof(ArchiveHeader), 1, archive) != 1) {
        fclose(archive);
        return 1;
    }

    if (header.magic_number != RLE_MAGIC_NUMBER) {
        fclose(archive);
        return 1;
    }

    FileHeader *headers = malloc(header.file_count * sizeof(FileHeader));
    if (!headers) {
        fclose(archive);
        return 1;
    }

    if (fread(headers, sizeof(FileHeader), header.file_count, archive) != header.file_count) {
        free(headers);
        fclose(archive);
        return 1;
    }

    // 解压文件
    for (int i = 0; i < header.file_count; i++) {
        if (headers[i].is_directory) {
            create_directory(headers[i].filename);
        } else {
            fseek(archive, headers[i].offset, SEEK_SET);

            // 创建父目录
            const char *last_slash = strrchr(headers[i].filename, '/');
            if (last_slash != NULL) {
                char dir_path[MAX_FILENAME_LENGTH];
                size_t dir_len = last_slash - headers[i].filename;
                if (dir_len < MAX_FILENAME_LENGTH) {
                    memcpy(dir_path, headers[i].filename, dir_len);
                    dir_path[dir_len] = '\0';
                    create_directory(dir_path);
                }
            }

            FILE *output = fopen(headers[i].filename, "wb");
            if (!output) continue;

            // 使用返回值
            rle_decompress(archive, output, headers[i].compressed_size);
            fclose(output);
        }
    }

    free(headers);
    fclose(archive);

    clock_t end_time = clock();  // 记录结束时间
    double elapsed_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;  // 计算耗时

    printf("解压完成！处理文件数: %d\n", header.file_count);
    printf("耗时: %.2f 秒\n", elapsed_time);

    return 0;
}
